/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package geradordao;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.util.List;

/**
 *
 * @author Iana
 */
public class GeradorCodigo {

    private No raiz;
    private PrintWriter out;
    private boolean gerarAtribs;
    private Boolean mapeia = false;
    private String classe = null;
    private String fileName = null;

    public GeradorCodigo(No raiz) {
        this.raiz = raiz;
    }

    public void gerar() {
        gerar(raiz);
    }

    private Object gerar(No no) {
        switch (no.getTipo()) {
            case No_QueryList:
                return QueryList(no);
            case No_Query:
                return Query(no);
            case No_CreateStm:
                return CreateStm(no);
            case No_FieldDefList:
                return fieldDefList(no);
            case No_FieldDefList2:
                return fieldDefList2(no);
            case No_FieldDef:
                return fieldDef(no);
            case No_Type:
                return type(no);
        }

        return null;
    }

    //<Query_List> ::= <Query> ';' <Query_List> |
    private Object QueryList(No no) {
        if (no.getFilhos().size() > 0) {
            gerar(no.getFilho(0));
            gerar(no.getFilho(1));
        }
        return null;
    }

    //<Query> ::= <Alter Stm> | <Create Stm>
    private Object Query(No no) {
        gerar(no.getFilho(0));
        return null;
    }

    //<Create Stm> ::= create table Id '(' <Field Def List> ')'
    private Object CreateStm(No no) {
        try {
            Token id = no.getFilho(2).getToken();
            fileName = toUpperCaseFirstChar(id.getImagem());
            mapeia = TabelaSimbolos.ehReferencia(fileName);

            out = new PrintWriter(new File("A:\\" + fileName + ".java"));

            if (mapeia) {
                out.write("\timport java.util.ArrayList;");
                out.write("\n\timport java.util.List;\n\n");
            }
            out.write("public class " + fileName + " {\n");

            // gera os atributos
            gerarAtribs = true;
            gerar(no.getFilho(4));
            if (mapeia) {
                List<String> referSimbs = TabelaSimbolos.getClassePorReferencia(fileName);
                for (int i = 0; i < referSimbs.size(); i++) {
                    out.write("\n\tprivate List<" + toUpperCaseFirstChar(referSimbs.get(i)) + "> " + toLowerCaseFirstChar(referSimbs.get(i)) + "s = new ArrayList<" + toUpperCaseFirstChar(referSimbs.get(i)) + ">();");
                }
            }

            // gera construtor
            out.write("\n\n\tpublic " + fileName + "() {");
            out.write("\n\t}");

            // gera gets e sets
            gerarAtribs = false;
            gerar(no.getFilho(4));

            if (mapeia) {
                List<String> referSimbs = TabelaSimbolos.getClassePorReferencia(fileName);
                for (int i = 0; i < referSimbs.size(); i++) {
                    out.write("\n\n\tpublic List<" + toUpperCaseFirstChar(referSimbs.get(i)) + "> get" + toUpperCaseFirstChar(referSimbs.get(i)) + "s() {");
                    out.write("\n\t\treturn " + toLowerCaseFirstChar(referSimbs.get(i)) + "s;");
                    out.write("\n\t}");

                    out.write("\n\n\tpublic void set" + toUpperCaseFirstChar(referSimbs.get(i)) + "s(List<" + toUpperCaseFirstChar(referSimbs.get(i)) + "> " + toLowerCaseFirstChar(referSimbs.get(i)) + "s) {");
                    out.write("\n\t\tthis." + toLowerCaseFirstChar(referSimbs.get(i)) + "s = " + toLowerCaseFirstChar(referSimbs.get(i)) + "s;");
                    out.write("\n\t}");
                }
            }

            out.write("\n}");

            out.flush();
            out.close();
            geraConexao();
            geraDAO(fileName);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

    //<Field Def List> ::= <Field Def> <Field Def List 2>
    private Object fieldDefList(No no) {
        gerar(no.getFilho(0));
        gerar(no.getFilho(1));
        return null;
    }

    //<Field Def List 2> ::= ',' <Field Def> <Field Def List 2> | <Constraint List>
    private Object fieldDefList2(No no) {
        if (no.getFilhos().size() > 1) {
            gerar(no.getFilho(1));
            gerar(no.getFilho(2));
        } else {
            gerar(no.getFilho(0));
        }
        return null;
    }

    //<Field Def> ::= Id <Type> <Not Null>
    private Object fieldDef(No no) {
        Token id = no.getFilho(0).getToken();
        String tipo = (String) gerar(no.getFilho(1));

        if (gerarAtribs) {
            List<Simbolo> referSimbs = TabelaSimbolos.getSimbolosChaveByEscopo(fileName);
            for (Simbolo ref : referSimbs) {
                if (ref.getToken().equals(id)) {
                    out.write("\n\tprivate " + ref.getReferencia() + " " + id.getImagem() + ";");
                }
            }
            if (TabelaSimbolos.ehAtributo(id)) {
                out.write("\n\tprivate " + tipo + " " + id.getImagem() + ";");
            }
        } else { // gerar gets e sets
            List<Simbolo> referSimbs = TabelaSimbolos.getSimbolosChaveByEscopo(fileName);
            for (Simbolo ref : referSimbs) {
                if (ref.getToken().equals(id)) {
                    out.write("\n\n\tpublic " + ref.getReferencia() + " get" + toUpperCaseFirstChar(id.getImagem()) + "() {");
                    out.write("\n\t\treturn " + id.getImagem() + ";");
                    out.write("\n\t}");

                    out.write("\n\n\tpublic void set" + toUpperCaseFirstChar(id.getImagem()) + "(" + ref.getReferencia() + " " + id.getImagem() + ") {");
                    out.write("\n\t\tthis." + id.getImagem() + " = " + id.getImagem() + ";");
                    out.write("\n\t}");
                }
            }
            if (TabelaSimbolos.ehAtributo(id)) {
                out.write("\n\n\tpublic " + tipo + " get" + toUpperCaseFirstChar(id.getImagem()) + "() {");
                out.write("\n\t\treturn " + id.getImagem() + ";");
                out.write("\n\t}");

                out.write("\n\n\tpublic void set" + toUpperCaseFirstChar(id.getImagem()) + "(" + tipo + " " + id.getImagem() + ") {");
                out.write("\n\t\tthis." + id.getImagem() + " = " + id.getImagem() + ";");
                out.write("\n\t}");
            }
        }

        return null;
    }

    private void geraConexao() {
        try {
            out = new PrintWriter(new File("A:\\ConnectionFactory.java"));

            out.write("import java.sql.*;\n");
            out.write("import java.util.logging.Level;\n");
            out.write("import java.util.logging.Logger;\n");

            out.write("\npublic class ConnectionFactory {");

            out.write("\nprivate Connection conexao;");
            out.write("\n\tpublic Connection getConnection() {");
            out.write("\n\t\ttry {");
            out.write("\n\t\t\tClass.forName(\"org.apache.derby.jdbc.ClientDriver\");");
            out.write("\n\t\t\tconexao = DriverManager.getConnection(\"jdbc:derby://localhost:1527/projetoTeste\",\"root\",\"123\");");
            out.write("\n\t\t\treturn conexao;");
            out.write("\n\t\t} catch(SQLException e) {");
            out.write("\n\t\t\tthrow new RuntimeException(e);");
            out.write("\n\t\t} catch(ClassNotFoundException ex) {");
            out.write("\n\t\t\tLogger.getLogger(ConnectionFactory.class.getName()).log(Level.SEVERE, null, ex);");
            out.write("\n\t\t}");
            out.write("\n\t\treturn null;");
            out.write("\n\t}");

            out.write("\n\n\tpublic void close() throws SQLException {");
            out.write("\n\t\tif (this.conexao != null){");
            out.write("\n\t\t\tthis.conexao.close();");
            out.write("\n\t\t}");
            out.write("\n\t}\n");

            out.write("\n\n\tpublic ResultSet executar(String query) throws SQLException {");
            out.write("\n\t\tStatement st;");
            out.write("\n\t\tResultSet rs;");
            out.write("\n\t\tthis.getConnection();");
            out.write("\n\t\tst = this.conexao.createStatement();");
            out.write("\n\t\trs = st.executeQuery(query);");
            out.write("\n\t\treturn rs;");
            out.write("\n\t}\n");

            out.write("\n\n\tpublic int executarNoQuery(String query) throws SQLException {");
            out.write("\n\t\tStatement st;");
            out.write("\n\t\tint rs = 0;");
            out.write("\n\t\tthis.getConnection();");
            out.write("\n\t\tst = this.conexao.createStatement();");
            out.write("\n\t\trs = st.executeUpdate(query);");
            out.write("\n\t\treturn rs;");
            out.write("\n\t}\n");

            out.write("\n}");
            out.flush();
            out.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

    private void geraDAO(String fileName) {
        try {
            out = new PrintWriter(new File("A:\\" + fileName + "DAO.java"));

            out.write("import java.sql.*;\n");
            out.write("import java.util.*;\n");
            out.write("\npublic class " + fileName + "DAO {\n");

            // gera ref. para conexão
            out.write("\n\tprivate ConnectionFactory conexao = new ConnectionFactory();");
            out.write("\n\tprivate Connection connection;");
            out.write("\n\tString query;\n");

            // gera construtor
            out.write("\n\tpublic " + fileName + "DAO() throws SQLException{");
            out.write("\n\t\tif(connection==null||connection.isClosed()){");
            out.write("\n\t\t\tthis.connection = conexao.getConnection();");
            out.write("\n\t\t}");
            out.write("\n\t}");

            geraInsert(fileName);
            geraUpdate(fileName);
            geraDelete(fileName);
            geraSelect(fileName);
            geraGetById(fileName);

            out.write("\n}");

            out.flush();
            out.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

    }

    private void geraSelect(String entidade) {
        String instancia = toLowerCaseFirstChar(entidade);
        out.write("\n\n\tpublic List<" + entidade + "> consultar() throws SQLException {");
        out.write("\n");

        List<Simbolo> atribSimbs = TabelaSimbolos.getSimbolosByEscopo(entidade);
        String sql = "select * from " + entidade;
        out.write("\n\t\tquery = \"" + sql + " \";");
        out.write("\n\t\tResultSet rset = conexao.executar(query);");
        out.write("\n");
        out.write("\n\t\tList<" + entidade + "> " + instancia + "s = new ArrayList<" + entidade + ">();");
        out.write("\n\t\twhile (rset.next()) {");
        out.write("\n\t\t\t" + entidade + " " + instancia + " = new " + entidade + "();");
        int pos = 1;
        for (Simbolo atrib : atribSimbs) {
            String atribName = atrib.getToken().getImagem();
            String atribTipo = atrib.getTipo();
            if (atribTipo.equals("Integer")) {
                atribTipo = "Int";
            }
            if (!atribTipo.equalsIgnoreCase("table")) {
                out.write("\n\t\t\t" + instancia + ".set" + toUpperCaseFirstChar(atribName) + "(rset.get" + atribTipo + "(" + pos + "));");
                pos++;
            }
        }

        out.write("\n\t\t\t" + instancia + "s.add(" + instancia + ");");
        out.write("\n\t\t}");

        out.write("\n");
        out.write("\n\t\tconexao.close();");
        out.write("\n\t\treturn " + instancia + "s;");

        out.write("\n");
        out.write("\n\t}");
    }

    private void geraInsert(String entidade) {
        String instancia = toLowerCaseFirstChar(entidade);
        out.write("\n\n\tpublic boolean insert(" + entidade + " " + instancia + ") throws SQLException {");
        out.write("\n");
        String variaveis = null;
        List<Simbolo> atribSimbs = TabelaSimbolos.getSimbolosByEscopo(entidade);

        for (Simbolo atrib : atribSimbs) {
            String atribName = atrib.getToken().getImagem();
            String atribTipo = atrib.getTipo();
            if (atribTipo.equals("Integer")) {
                atribTipo = "Int";
            }
            if (!atribTipo.equalsIgnoreCase("table")) {
                if (variaveis != null) {
                    variaveis = variaveis + "," + atribName;
                } else {
                    variaveis = atribName;
                }
            }
        }
        String parametros = null;
        int pos = 1;
        for (Simbolo atrib : atribSimbs) {
            String atribName = atrib.getToken().getImagem();
            String atribTip = atrib.getTipo();
            if (atribTip.equals("Integer")) {
                atribTip = "Int";
            }
            if (!atribTip.equalsIgnoreCase("table")) {
                if (parametros != null) {
                    parametros = parametros + " + \",\" + " + instancia + ".get" + toUpperCaseFirstChar(atribName) + "()";
                } else {
                    parametros = "\" + " + instancia + ".get" + toUpperCaseFirstChar(atribName) + "()";
                }
                pos++;
            }
        }

        out.write("\n\t\tquery = \"insert into " + entidade + "(" + variaveis + ") values (" + parametros + " + \")\";");
        out.write("\n\t\tInteger linha = conexao.executarNoQuery(query);");
        out.write("\n");
        out.write("\n\t\tconexao.close();");
        out.write("\n\t\treturn linha > 0;");

        out.write("\n");
        out.write("\n\t}");
    }

    private void geraUpdate(String entidade) {
        String instancia = toLowerCaseFirstChar(entidade);
        out.write("\n\n\tpublic boolean update(" + entidade + " " + instancia + ") throws SQLException {");
        out.write("\n");
        String variaveis = null;
        List<Simbolo> atribSimbs = TabelaSimbolos.getSimbolosByEscopo(entidade);

        for (Simbolo atrib : atribSimbs) {
            String atribName = atrib.getToken().getImagem();
            String atribTipo = atrib.getTipo();
            if (atribTipo.equals("Integer")) {
                atribTipo = "Int";
            }
            if (!atribTipo.equalsIgnoreCase("table") && (!atrib.getClassificacao().equalsIgnoreCase("primary"))) {
                if (variaveis != null) {
                    variaveis = variaveis + " \", " + atribName + " = \" + " + instancia + ".get" + toUpperCaseFirstChar(atribName) + "() + ";
                    //parametros = parametros + " + \",\" + " + instancia + ".get" + toUpperCaseFirstChar(atribName) + "()";
                    //nome = '" + cliente.getNome() + "', endereco = '" + cliente.getEndereco() + "' where id = " + cliente.getId();
                } else {
                    variaveis = atribName + " = \" + " + instancia + ".get" + toUpperCaseFirstChar(atribName) + "() + ";
                    //parametros = "\" + " + instancia + ".get" + toUpperCaseFirstChar(atribName) + "()";
                }
            }
        }
        out.write("\n\t\tquery = \"update " + entidade + " set " + variaveis + "\" where id = \" + " + instancia + ".get" + toUpperCaseFirstChar(atribSimbs.get(1).getToken().getImagem()) + "();");
        //query = "update Cliente set nome = '" + cliente.getNome() + "', endereco = '" + cliente.getEndereco() + "' where id = " + cliente.getId();
        out.write("\n\t\tInteger linha = conexao.executarNoQuery(query);");
        out.write("\n");
        out.write("\n\t\tconexao.close();");
        out.write("\n\t\treturn linha > 0;");

        out.write("\n");
        out.write("\n\t}");
    }

    private void geraDelete(String entidade) {
        String instancia = toLowerCaseFirstChar(entidade);
        out.write("\n\n\tpublic boolean delete(" + entidade + " " + instancia + ") throws SQLException {");
        out.write("\n");

        List<Simbolo> atribSimbs = TabelaSimbolos.getSimbolosByEscopo(entidade);
        Simbolo atrib = atribSimbs.get(1);
        String atribName = atrib.getToken().getImagem();
        String atribTipo = atrib.getTipo();
        if (atribTipo.equals("Integer")) {
            atribTipo = "Int";
        }
        String sql = "delete from " + entidade + " where " + atribSimbs.get(1).getToken().getImagem() + " = \" + " + instancia + ".get" + toUpperCaseFirstChar(atribName) + "()";
        out.write("\n\t\tquery = \"" + sql + ";");
        out.write("\n\t\tint linhas = conexao.executarNoQuery(query);");
        out.write("\n");
        out.write("\n\t\tconexao.close();");
        out.write("\n\t\treturn linhas > 0;");

        out.write("\n");
        out.write("\n\t}");
    }

    private void geraGetById(String entidade) {
        String instancia = toLowerCaseFirstChar(entidade);
        List<Simbolo> atribSimbs = TabelaSimbolos.getSimbolosByEscopo(entidade);
        String idTipo = atribSimbs.get(1).getTipo();
        String idName = atribSimbs.get(1).getToken().getImagem();

        out.write("\n\n\tpublic " + entidade + " getById(" + idTipo + " id" + ") throws SQLException {");
        out.write("\n");

        String sql = "select * from " + entidade + " where " + idName + " = \" + id";
        out.write("\n\t\tquery = \"" + sql + ";");
        if (idTipo.equals("Integer")) {
            idTipo = "Int";
        }
        out.write("\n\t\tResultSet rs = conexao.executar(query);");
        out.write("\n");
        out.write("\n\t\t" + entidade + " " + instancia + " = null;");
        out.write("\n\t\tif (rs.next()) {");

        out.write("\n\t\t\t" + instancia + " = new " + entidade + "();");
        int pos = 1;
        for (Simbolo atrib : atribSimbs) {
            String atribName = atrib.getToken().getImagem();
            String atribTipo = atrib.getTipo();
            if (atribTipo.equals("Integer")) {
                atribTipo = "Int";
            }
            if (!atribTipo.equalsIgnoreCase("table")) {
                out.write("\n\t\t\t" + instancia + ".set" + toUpperCaseFirstChar(atribName) + "(rs.get" + atribTipo + "(" + pos + "));");
                pos++;
            }
        }
        out.write("\n\t\t}");

        out.write("\n");
        out.write("\n\t\tconexao.close();");
        out.write("\n\t\treturn " + instancia + ";");

        out.write("\n");
        out.write("\n\t}");
    }

    // <Type> ::= bit | date | time | timestamp | decimal | real | float |
    // smallint | integer | int | interval | character | varchar <Tamanho>
    private Object type(No no) {
        return TabelaSimbolos.getTipoJavaEquivalente(no.getFilho(0).getToken().getImagem());
    }

    private String toUpperCaseFirstChar(String idImagem) {
        String firstUpper = idImagem.substring(0, 1).toUpperCase();
        String tail = idImagem.substring(1, idImagem.length());
        return firstUpper + tail;
    }

    private String toLowerCaseFirstChar(String idImagem) {
        String firstLower = idImagem.substring(0, 1).toLowerCase();
        String tail = idImagem.substring(1, idImagem.length());
        return firstLower + tail;
    }
}
